第3章 オブジェクト指向
抽象を意識したプログラミングを実践するのに、オブジェクト指向が役に立つ(扉)
ひさてるさんの主張「オブジェクト指向を定義することはできない」
3-1 オブジェクト指向の定義はない
プログラムの構造をモノに見立てる、3つのメリット(後述)
物が物体として存在するイメージ
メモリ上のバイト列やCPUのステップは人間には認識しにくい
オブジェクト指向、3つの特徴(関連して同時に現れる (3-4))
カプセル化だけでオブジェクト指向を説明しようとすると、構造化プログラミングの話になってしまう、というのを3章の最後に書いています。3つの見方をいっぺんにやるのが、賢い人たちがオブジェクト指向いいねと思った感覚にいちばん近くなるという話です
カプセル化(3-2)
オブジェクト指向では、関係の強い変数と処理の両方をいちどに、オブジェクトという概念単位にまとめます。(Kindle 版 p.92)
ひとつの変数名を与えられる
=認識単位
例:「自動車」として認識する例(2章のキーボードの例)
全再利用の原則と閉鎖性共通の原則
オブジェクトは凝集度の最小単位
詳細の隠蔽(内部を意識させない)
この隠蔽のおかげで、運転者は限られた操作方法を間違えずやることだけに集中できます。(Kindle 版 p.94)
(👉53 正しい使い方を簡単に、誤った使い方を困難に)
カプセル化とは「カプセルにすること」である
責務の移譲
オブジェクト指向で設計されたソフトウェアは、カプセル間の委譲のかたまりです。(Kindle 版 p.95)
境界線を引く=関心の分離
IMO:詳細を知らなくても動かせる。詳細は詳細だけに集中して尖らせられる
知識最小の原則=デメテルの法則=Tell, Don't Ask.
オブジェクトはできる限り、自身のメソッドだけで使用者の要求に答えられるように作り、使用者がオブジェクトの各種プロパティをあれこれ聞かなくてよい関係を築きなさいという教えです。(Kindle 版 p.96)
構造体はいちいちプロパティを聞かなくてはならない(メソッドを持たないから)
Pythonの辞書にも言える
詳細をほじくり出さない/出させない
多態性=ポリモーフィズム(3-3)
呼び出し側で「同じと認識してよい」と見立てたものに、複数種類の実体がある点だけに着目した概念 (Kindle 版 p.106)
代表格はプラグイン
感想:プラグインを開発した経験から非常にしっくり来た!(例えばVS Code拡張)
Entry Points for Plugins
本体のソフトウェアから見ると、どのプラグインもプラグインという単一種類に見える
本体のソフトウェアは個々のプラグインの詳細に興味はない
プラグイン1つ1つは中身が違う実体
呼び出し側のコードの変更をまったくせず、追加や切り替えだけで動作のバリエーションを増やせる (Kindle 版 p.99)
ペットショップの例(情報モデル)
仕様:PetshopCustomerがtouchするとどんなPetであろうとreactionが起きる
IMO:reaction(名詞)よりreact(動詞)の方が好み
多態性はプログラムのロジックを簡単にする
ペットショップの例には分岐がない
ログを書くか書かないかを切り替える例(技術的な問題)
システムのロガーと代替可能な、何もしないNullLoggerがあると考えた例 (Kindle 版 p.105)
この発想により、分岐がなくなりロジックが簡単になり読みやすい
オブジェクト指向らしさに慣れたプログラマーにとっては、部品の詳細がどうなっているかを気にせず、焦点を当てた責務にだけ集中するのが、とても自然な感覚です。(Kindle 版 p.105)
多態性は型には依存しない
ダックタイピングでも多態性は利用できる
👉 typing.Protocolを使って実装してみた
疑問:Protocolだと「継承/汎化」にある抽象でモノがあることにできるはできない?(多態に振っているのだろうか?)
型チェックが必要(p.117)ならProtocolだと不足している?(runtime checkすればよい?)
継承/汎化(3-4)
多態性が語ったのは不安定パッケージ=具象側にバリエーションが増える話で、対称的に、継承が語っているのは、安定パッケージ=抽象側に優れた基底を確保する話です。(Kindle 版 p.115)
3-4のイラスト「抽象を使えばモノがあることにできる」
差分プログラミングはおまけ
オブジェクト継承の醍醐味は、(略) 概念の一般化と、そのimplements(実装)の分離 (Kindle 版 p.109)
概念の一般化=抽象クラス
具象クラスは抽象クラスの一種:is-a関係
(is-a関係 👉 ListはIteratorですか?)
抽象と具象は相対的
継承はその逆、isa関係による概念の集約と一般化に着目します。(Kindle 版 p.110)
先のペットショップの例
PetshopCustomerには適切な抽象のPetが設けてあることが重要
Petはreactionを持つ
どのような具象かは関心がない
よい抽象を見つける上で、汎化
こうした継承の逆向きの、現実のバリエーションから帰納的に抽象を発見していくアプローチを、汎化と言います。(Kindle 版 pp.111-112)
システム化対象の中で最小の抽象概念を見つける
よい抽象を見つけたらパッケージを安定させられるので、安定依存の原則(2章)に役立つ
具象は1つだけでもよい(「モノがあることにできる」の真意)
(感想:多態性の「poly」は0〜無限大ってことなのかな。monoの対義語)
ニュースサイトの記事の取得・表示の例
抽象
ArticleRepositoryInterface
ArticlePresentaterInterface
仮にインターフェースに依存しておく作戦なら、データベースの事情と画面の事情がわからないうちでも、先にArticleOperationの大枠を安定させられます。(Kindle 版 p.115)
抽象があるということは、継承によってオーバーライドすれば詳細はどうとでもなる
抽象を抽象のまま使える
詳細に向く関心も依存もまるでないのが、抽象を使うメリット (Kindle 版 p.117)
抽象と具象の関係には、明確に定義されたインターフェースがあることが重要です。(Kindle 版 p.117)
型が必要
オブジェクト指向は構造化プログラミングではない(3-5)
「ここまでは構造化プログラミングで、ここからはオブジェクト指向だ」という、この発想の転換ポイント (Kindle 版 p.124)
感想:構造化プログラミングはオワコンでオブジェクト指向万歳という主張では決してない。ここが違うということを示しただけ
閉鎖性共通の原則により、閉じたパッケージの中では密な上下構造(構造化プログラミングの概念)が必須
構造化
ifやfor、ブロック構造で閉じる
構造体(変数のグループ化)
手続きをサブルーチンに分ける
フラットにすべてを見渡さなければならない場合より、はるかに大きなソフトウェア構造物を作れるというのが、広義の構造化プログラミングの意味です。(Kindle 版 p.120)
ブラックボックス化の活用
構造体やサブルーチンは使う側から見ると抽象
構造化プログラミングは機能分解
トップダウンアプローチによるひとつの責務の分担作業 (Kindle 版 p.122)
上位構造の正しさが下位構造の正しさに依存する
欠点はこれを解消できないこと
(IMO:分割統治とは違うんだろうな)
アラン・ケイのオブジェクト指向のイメージ
ボトムアップ
後からインターフェースに適合するものを当てはめるだけでよい
プラモデルに電池をはめる(電池の作り方を知る必要はない)
違いは、機能分解(上位構造の正しさが下位構造に依存)ではなく、「上位構造が正しいことを下位構造なしに成立させられる (Kindle 版 p.123)」
構造化プログラミング用語の「抽象」は、オブジェクト指向では「具象クラス」にあたります。(Kindle 版 p.123)
みなし抽象
オブジェクト指向の「抽象」は、機能する抽象
下位に混入したバグと上位(抽象)の利用者は無関係
観念的な存在ではなく「実際に開発工程で機能する抽象が実在する」 (Kindle 版 p.123)
関連するめもりーちゃん
オブジェクト指向プログラミングではなく振る舞いデータ型による構造化プログラミング
obj.run(option1), obj.run(option2):データが同じでオプションが違う、これが構造化プログラミング
h.handle(data1), h.handle(data2)
オブジェクト指向はどこに何が書かれているか知らなくても ここに書かれた関心だけ見ればいいやつ!
補足: OOPは、今いじってる箇所とその関係者の範囲であれば、けっこうわかりやすい。ただし、初見で全体マップを把握したいという期待に応えられるかというと、それはOOPとは別の話やな。考えずにやると、むしろキメキメ構造化の方がマシってなるかもな、です